﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using Android.Util;
using LeePong.Game.Engines;
using LeePong.Game.Entities;
using LeePong.Game.Shapes;

namespace LeePong.Game
{
    public class Engine
    {
        public Ball Ball;
        public Land Land;
        public Racket LeftRacket;
        public Racket RightRacket;

        public VirtualPlayer VirtualPlayer;

        public Score LeftScore;
        public Score RightScore;

        private CollisionDetector _collisionDetector;

        private const int TOUCH_TOLERANCE = 40;

        private Rectangle _racketLTouchZone;
        private Rectangle RacketLTouchZone
        {
            get
            {
                if (_racketLTouchZone == null)
                {
                    var r = this.LeftRacket.Rect;
                    var lr = this.Land.Rect;
                    _racketLTouchZone = new Rectangle(r.Left, lr.Top, r.Width + TOUCH_TOLERANCE, lr.Height);
                }
                return _racketLTouchZone;
            }
        }

        private Rectangle _racketRTouchZone;
        private Rectangle RacketRTouchZone
        {
            get
            {
                if (_racketRTouchZone == null)
                {
                    var r = this.RightRacket.Rect;
                    var lr = this.Land.Rect;
                    _racketRTouchZone = new Rectangle(r.Left - TOUCH_TOLERANCE, lr.Top, r.Width + TOUCH_TOLERANCE, lr.Height);
                }
                return _racketRTouchZone;
            }
        }


        public bool IsBusy;
        private Canceller Cancel;

        public Engine()
        {

            this.Ball = new Ball();

            this.LeftRacket = new Racket();
            this.RightRacket = new Racket();
            this.Cancel = new Canceller();
            Init(400, 300);
        }

        public void Start()
        {
        }

        public void Init(int width, int height)
        {
            this.Land = new Land(new Rectangle(0, 0, width, height));

            InitRaquet(this.LeftRacket);
            InitRaquet(this.RightRacket);
            this.LeftRacket.Rect.Coord.X = this.Land.Rect.Left;
            this.LeftRacket.Rect.CenterY = this.Land.Rect.CenterY;
            this.RightRacket.Rect.Coord.X = this.Land.Rect.Right - this.RightRacket.Rect.Width;
            this.RightRacket.Rect.CenterY = this.Land.Rect.CenterY;

            this.Ball.Rect.Width = this.Land.Rect.Height / 8;
            this.Ball.Rect.Height = this.Ball.Rect.Width;
            this.Ball.Rect.CenterX = this.Land.Rect.CenterX;
            this.Ball.Rect.CenterY = this.Land.Rect.CenterY;

            var leftscoreRect = new Rectangle();
            leftscoreRect.Width = this.Land.Rect.Width / 4;
            leftscoreRect.Height = leftscoreRect.Width;
            leftscoreRect.Coord.X = this.Land.Rect.Right * 0.4f;
            leftscoreRect.Coord.Y = this.Land.Rect.CenterY;
            LeftScore = new Score(leftscoreRect);


            var rightScoreRect = new Rectangle();
            rightScoreRect.Width = leftscoreRect.Width;
            rightScoreRect.Height = leftscoreRect.Width;
            rightScoreRect.Coord.X = this.Land.Rect.Right * 0.8f;
            rightScoreRect.Coord.Y = this.Land.Rect.CenterY;
            RightScore = new Score(rightScoreRect);

            var profile = new PlayerProfile() { MoveSpeed = 0.04f, Power = 0.4f };
            profile.ShotSelection = new List<Shot> { Shot.Reflect, Shot.Reflect, Shot.Cross, Shot.Cross, Shot.Counter, Shot.Long };
            VirtualPlayer = new VirtualPlayer(this, profile);

            _collisionDetector = new CollisionDetector(this, new CollisionManager(this));
        }

        private void InitRaquet(Racket racket)
        {
            racket.Rect.Width = this.Land.Rect.Width / 7;
            racket.Rect.Height = this.Land.Rect.Height / 3;
            this.RightRacket.Rect.Coord.Y = this.Land.Rect.Top / 2;
        }

        public void LaunchBall()
        {
            if (IsBusy)
            {
                this.Ball.Speed = 0;
                IsBusy = false; ;
                return;
            }

            IsBusy = true;
            this.Ball.Angle = 0;
            this.Ball.Speed = this.Land.Rect.Width / 100;

            var thread = new Thread(() =>
            {
                LaunchBallAction();
            });
            thread.Start();
        }

        public void LaunchBallAction()
        {
            var originalStep = this.Ball.Speed;
            int i = 0;
            var sw = new Stopwatch();
            sw.Start();
            do
            {
                i++;
                if (i == 1000)
                {
                    Log.D("----", "ball fps:" + 1000 / (sw.ElapsedMilliseconds / 1000.0));
                    sw.Restart();
                    i = 0;
                }
                Thread.Sleep(10);

                CoordinatesMover.Move(Ball.Rect.Coord, this.Ball.Angle, this.Ball.Speed);
                this.Ball.NormalizeAngle();
                this.Ball.NormalizeSpeed(originalStep, 0.05f);
                _collisionDetector.Check();

                this.VirtualPlayer.Move();
            }
            while (IsBusy);
        }



        public void MoveRaquet(Racket racket, float y)
        {
            this.Cancel.Token.IsCancelled = false;
            var targetCoord = new Coordinates() { X = racket.Rect.Coord.X, Y = y };
            CoordinatesMover.MoveAsync(racket.Rect.Coord, targetCoord, 20, () => { }, this.Cancel.Token);
        }

        public void MoveRaquetFast(Racket racket, float y)
        {
            this.Cancel.Token.IsCancelled = true;
            racket.Rect.Coord.Y = y;
            // CoordinatesMover.MoveAsync(racket.Rectangle.Coord, targetCoord, 10, () => IsBusy = false);
        }

        public void TouchSingleTap(Coordinates touchCoords)
        {
            var raquet = this.IsTouchOnRaquet(touchCoords);
            if (raquet != null)
            {
                var y = ComputeRaquetY(raquet, touchCoords.Y);
                MoveRaquetFast(raquet, y);
                return;
            }

            raquet = this.IsTouchForRaquet(touchCoords);
            if (raquet != null)
            {
                var y = ComputeRaquetY(raquet, touchCoords.Y);
                MoveRaquet(raquet, y);
            }
            else
            {
                LaunchBall();
            }
        }

        public void TouchScroll(Coordinates newCoord, Coordinates oldCoord)
        {
            var raquet = this.IsTouchOnRaquet(oldCoord);
            if (raquet != null)
            {
                var y = ComputeRaquetY(raquet, newCoord.Y);
                MoveRaquetFast(raquet, y);
                return;
            }

            raquet = this.IsTouchForRaquet(oldCoord);
            if (raquet != null)
            {
                var y = ComputeRaquetY(raquet, newCoord.Y);
                MoveRaquetFast(raquet, y);
            }
        }

        private Racket IsTouchForRaquet(Coordinates touchCoords)
        {
            //if (this.RacketLTouchZone.Contains(touchCoords))
            //{
            //    return this.LeftRacket;
            //}

            if (this.RacketRTouchZone.Contains(touchCoords))
            {
                return this.RightRacket;
            }

            return null;
        }

        private Racket IsTouchOnRaquet(Coordinates touchCoords)
        {
            //if (this.LeftRacket.Rect.Contains(touchCoords))
            //{
            //    return this.LeftRacket;
            //}

            if (this.RightRacket.Rect.Contains(touchCoords))
            {
                return this.RightRacket;
            }

            return null;
        }

        private float ComputeRaquetY(Racket racket, float inputY)
        {
            var landRec = this.Land.Rect;
            var raquetHeight = racket.Rect.Height;
            inputY = inputY - raquetHeight / 2;

            inputY = Math.Max(inputY, landRec.Top);
            inputY = Math.Min(inputY, landRec.Bottom - raquetHeight);
            return inputY;
        }
    }

    public class Canceller
    {
        public RunToken Token;

        public Canceller()
        {
            Token = new RunToken();
        }
    }

    public class RunToken
    {
        public bool IsCancelled;
    }
}
